// Copyright (c) 2010, Anthony Cassidy
// All rights reserved.
// 
// Redistribution and use in source and binary forms, with or without modification, are 
// permitted provided that the following conditions are met:
// 
//     * Redistributions of source code must retain the above copyright notice, this list 
//         of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright notice, this 
//         list of conditions and the following disclaimer in the documentation and/or 
//         other materials provided with the distribution.
//     * Neither the name of the AntsAdventureGameCreator nor the names of its contributors 
//         may be used to endorse or promote products derived from this software without 
//         specific prior written permission.
// 
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 
// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
// SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 
// THE POSSIBILITY OF SUCH DAMAGE.
#include "XStdafx.h"
#include "XThreadForOnEnterRoomAndOnCommandLine.h"
#include "AntsApi.h"//gXXGui objects
#include "RoomRegistry.h"//Global Event Handler
#include "InventoryItems.h"
#include "InventoryItem.h"
#include "RoomObjects.h"
#include "RoomObject.h"
#include "Room.h"
#include "Inventory.h"
#include "CommandLine.h"
#include "Verbs.h"
#include "XRoom.h" //GetListOfRooms
#include "XGlobals.h"
extern Verbs g_verbs;

namespace NonGuiThreadEvent
{
    static const int InitializeRootVariables = 1;
    static const int EveryTick = 6;
    static const int EnterRoom = 7;
    static const int CommandLineExecute= 2;
    static const int CommandLineExecuteCodeOnly= 3;
    static const int DebugMenuExtension = 4;
    static const int ExitRoom = 5;

};

#include "XDiagnostics.h"



XThreadForOnEnterRoomAndOnCommandLine::XThreadForOnEnterRoomAndOnCommandLine( XVerbs& verbs, XInventory& inventory, XRoom& room, XCommandLine& commandLine, QObject& parent)
:QThread(&parent)
, m_unmarshalledVerbs  ( verbs)
, m_unmarshalledInventory ( inventory )
, m_unmarshalledRoom ( room )
, m_unmarshalledCommandLine ( commandLine )

{
    m_isRestart = false;
    m_isAbort = false;
    m_id = 0;

    // add this so the gui thread is told when the enter room handler has completed.
    bool isOk = QObject::connect(this, SIGNAL(EnterRoomFinished()), &parent, SLOT(OnEnterRoomFinished()));
    ASSERT(isOk);
    (isOk=isOk);

    bool isOk2 = QObject::connect(this, SIGNAL(InitializeRootVariablesFinished()), &parent, SLOT(OnInitializeRootVariablesFinished()));
    ASSERT(isOk2);
    (isOk2=isOk2);


}

XThreadForOnEnterRoomAndOnCommandLine::~XThreadForOnEnterRoomAndOnCommandLine()
{
    XDiagnostics::Delete("XThreadForOnEnterRoomAndOnCommandLine");}

void XThreadForOnEnterRoomAndOnCommandLine::StartThread()
{
    m_isRestart = false;
    start(LowPriority);
}

void XThreadForOnEnterRoomAndOnCommandLine::InitializeAPIRootVariables()
{
    m_id = NonGuiThreadEvent::InitializeRootVariables;
    m_isRestart = true;
    m_condition.wakeOne();
}


void XThreadForOnEnterRoomAndOnCommandLine::CallOnCommandLineExecute(int v, QString a, QString b, int x, int y)
{
    m_id = NonGuiThreadEvent::CommandLineExecute;
    m_a = a;
    m_b = b;
    m_v = v;
    m_x = x;
    m_y = y;

    m_isRestart = true;
    m_condition.wakeOne();
}

void XThreadForOnEnterRoomAndOnCommandLine::CallOnCommandLineExecute(int id)
{
    m_id = NonGuiThreadEvent::CommandLineExecuteCodeOnly;
    m_v = id;

    m_isRestart = true;
    m_condition.wakeOne();
}

void XThreadForOnEnterRoomAndOnCommandLine::CallOnDebugMenuExtension(int index)
{
    m_id = NonGuiThreadEvent::DebugMenuExtension;
    m_v = index;

    m_isRestart = true;
    m_condition.wakeOne();
}

void XThreadForOnEnterRoomAndOnCommandLine::CallOnEnterRoom(QString room)
{
    Log::Threads(QString("GUIthread about to restart script thread for OnEnterRoom( %1)").arg(room));
    m_id = NonGuiThreadEvent::EnterRoom;
    m_room = room;

    ASSERT(isRunning());
    m_isRestart = true;
    m_condition.wakeOne();
}

void XThreadForOnEnterRoomAndOnCommandLine::CallOnExitRoom()
{
    Log::Threads(QString("GUIthread about to restart script thread for OnExitRoom()"));
    m_id = NonGuiThreadEvent::ExitRoom;

    ASSERT(isRunning());
    m_isRestart = true;
    m_condition.wakeOne();
}

void XThreadForOnEnterRoomAndOnCommandLine::run()
{
    Log::Threads("void XThreadForOnEnterRoomAndOnCommandLine::run()");

    for(;;)
    {
        // copy data from main thread
        m_mutex.lock();
        int id = this->m_id;
        QString a = this->m_a;
        QString b = this->m_b;
        int v = this->m_v;
        int x = this->m_x;
        int y = this->m_y;
        QString roomName = this->m_room;
        m_mutex.unlock();

        QVector<QString> listOfRooms = XRoom::ReturnListOfNamesOfRoomAndResourceRooms(roomName);

        // run execution request
        switch(id)
        {
        case NonGuiThreadEvent::InitializeRootVariables:
            // initialize objects with scenegraph data (constant so no need for mutex) and
            // create a QT emitter object in this thread, so it knows to marshal data when making callbacks
            XGlobals::SetVerbs( new Verbs(m_unmarshalledVerbs));//g_verbs deletes this
            XGlobals::SetCommandLine( new CommandLine(m_unmarshalledCommandLine));// g_commandline deletes this
            XGlobals::SetRoom( new Room(m_unmarshalledRoom));// g_room deletes this
            XGlobals::SetInventory( new Inventory(m_unmarshalledInventory) ); // g_inventory deletes this
            emit InitializeRootVariablesFinished();
            break;
        case NonGuiThreadEvent::DebugMenuExtension:
            AntsApi::GetCommandLine().SetMouseable(false);
            try
            {
                for(int i=0;i<listOfRooms.size();i++)
                {
                    if(GetRoomRegistry().At(listOfRooms[i]) != NULL)
                    {
                        GetRoomRegistry().At(listOfRooms[i])->OnDebugMenuExtension(v, false);
                    }
                }
            }
            catch(std::exception)
            {
            }
            catch(const char*)
            {
            }

            AntsApi::GetCommandLine().SetMouseable(true);
            AntsApi::GetCommandLine().Clear();
            break;
        case NonGuiThreadEvent::CommandLineExecute:
            Log::Threads("about to call OnCommandLineExecute");
            //Api::GetVerbs() = Verbs(m_unmarshalledVerbs);
            //Api::GetCommandLine() = CommandLine(m_unmarshalledCommandLine);
            AntsApi::GetCommandLine().SetMouseable(false);
            try
            {
                for(int i=0;i<listOfRooms.size();i++)
                {
                    if(GetRoomRegistry().At(listOfRooms[i]) != NULL)
                    {
                        GetRoomRegistry().At(listOfRooms[i])->OnCommandLineExecute(v, a, b, x, y);
                    }
                }
            }
            catch(std::exception)
            {
            }
            catch(const char*)
            {
            }

            AntsApi::GetCommandLine().SetMouseable(true);
            AntsApi::GetCommandLine().Clear();
            break;
        case NonGuiThreadEvent::EnterRoom:
            Log::Threads("about to call OnEnterRoom");
            try
            {
                for(int i=0;i<listOfRooms.size();i++)
                {
                    if(GetRoomRegistry().At(listOfRooms[i]) != NULL)
                    {
                        GetRoomRegistry().At(listOfRooms[i])->OnEnterRoom(roomName);
                    }
                }
            }
            catch(std::exception)
            {
                ASSERT(false && "should not be exceptions in OnEnterRoom");
            }
            catch(const char* message)
            {
                qDebug(message);
            }

            // When to trigger EnterRoomFinished event.
            // - We want to recover from errors as much as we can in main script 
            // - But we don't want to trigger this is we've just switched rooms.
            // (reason being because event won't get entered in to until just before the switchedRoom OnEnterRoom is called)
            if(!XReceiver::IsSayingAbort())
            {
                emit EnterRoomFinished();
            }
            Log::Threads("finished StartTimer");
            break;

        default:
            Log::Threads("default");
        }

        // wait here until we get another execution request
        m_mutex.lock();
        if (!m_isRestart)
            m_condition.wait(&m_mutex);
        Log::Threads("Threads:void XThreadForOnEnterRoomAndOnCommandLine::restarting");
        m_isRestart = false;
        m_mutex.unlock();
    }

}
